package org.jeecg.modules.alarm.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhouwr.common.enums.MessageEventType;
import com.zhouwr.common.enums.StartStopState;
import com.zhouwr.common.utils.DyMethodUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.modules.alarm.entity.*;
import org.jeecg.modules.alarm.enums.AlarmActuatorType;
import org.jeecg.modules.alarm.handler.ActuatorContext;
import org.jeecg.modules.alarm.mapper.AlarmConfigActuatorMapper;
import org.jeecg.modules.alarm.mapper.AlarmConfigMapper;
import org.jeecg.modules.alarm.mapper.AlarmConfigTriggerMapper;
import org.jeecg.modules.alarm.service.*;
import org.jeecg.modules.alarm.vo.AlarmConfigActuatorVo;
import org.jeecg.modules.alarm.vo.AlarmConfigTriggerVo;
import org.jeecg.modules.alarm.vo.AlarmConfigVo;
import org.jeecg.modules.device.entity.DeviceData;
import org.jeecg.modules.device.service.IDeviceDataService;
import org.jeecg.modules.device.service.IDeviceFunctionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 告警配置
 * @Author: jeecg-boot
 * @Date: 2021-01-11
 * @Version: V1.0
 */
@Service
@Slf4j
public class AlarmConfigServiceImpl extends ServiceImpl<AlarmConfigMapper, AlarmConfig> implements IAlarmConfigService {

    @Autowired
    private AlarmConfigMapper alarmConfigMapper;
    @Autowired
    private AlarmConfigTriggerMapper triggerMapper;
    @Autowired
    private AlarmConfigActuatorMapper actuatorMapper;
    @Autowired
    private IActuatorFunctionService invokeFunctionService;
    @Autowired
    private IActuatorNotificationService notificationService;
    @Autowired
    private IActuatorDataForwardService dataForwardService;
    @Autowired
    private IDeviceDataService dataService;
    @Autowired
    private IDeviceFunctionService functionsService;
    @Autowired
    private IActuatorFunctionService actuatorFunctionService;
    @Autowired
    private IAlarmRecordService alarmRecordService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addActuators(String configId, List<AlarmConfigActuatorVo> actuators) {
        actuators.forEach(actuatorVo -> {
            log.info("actuatorVo >>> {}", actuatorVo);
            actuatorVo.setId(null);
            AlarmConfigActuator actuator = new AlarmConfigActuator(actuatorVo);
            actuator.setAlarmConfigId(configId);
            actuatorMapper.insert(actuator);
            switch (actuator.getType()) {
                case DEVICE_FUNCTION:
                    invokeFunctionService.save(new ActuatorInvokeFunction(actuator.getId(), actuatorVo.getInvokeFunction()));
                    break;
                case NOTIFICATION:
                    notificationService.save(new ActuatorNotification(actuator.getId(), actuatorVo.getNotification()));
                    break;
                case DATA_FORWARD:
                    dataForwardService.save(new ActuatorDataForward(actuator.getId(), actuatorVo.getDataForward()));
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + actuator.getType());
            }
        });
    }

    /**
     * @param alarmConfigVo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveMain(AlarmConfigVo alarmConfigVo) {
        /* 保存配置主表信息 */
        AlarmConfig alarmConfig = new AlarmConfig(alarmConfigVo);
        alarmConfigMapper.insert(alarmConfig);
        /* 保存触发器 */
        alarmConfigVo.getTriggers().forEach(triggerVo -> {
            AlarmConfigTrigger trigger = new AlarmConfigTrigger(triggerVo);
            trigger.setId(null);
            trigger.setAlarmConfigId(alarmConfig.getId());
            triggerMapper.insert(trigger);
        });
        /* 保存执行器 */
        addActuators(alarmConfig.getId(), alarmConfigVo.getActuators());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateMain(AlarmConfigVo alarmConfigVo) {
        /* 修改配置主表信息 */
        AlarmConfig alarmConfig = new AlarmConfig(alarmConfigVo);
        alarmConfigMapper.updateById(alarmConfig);

        /* 修改触发器 */
        /* 1、先删除 */
        triggerMapper.deleteByConfigId(alarmConfig.getId());
        /* 2、添加 */
        alarmConfigVo.getTriggers().forEach(triggerVo -> {
            AlarmConfigTrigger trigger = new AlarmConfigTrigger(triggerVo);
            trigger.setAlarmConfigId(alarmConfig.getId());
            triggerMapper.insert(trigger);
        });

        /* 修改执行器 */
        /* 1、查询执行器 */
        actuatorMapper.selectByConfigId(alarmConfigVo.getId()).forEach(alarmConfigActuator -> {
            /* 2、删除执行器下所有的配置信息 */
            this.deleteActuatorProperties(alarmConfigActuator.getId(), alarmConfigActuator.getType());
        });
        /* 3、删除执行器 */
        actuatorMapper.deleteByConfigId(alarmConfig.getId());
        /* 4、重新添加执行器 */
        addActuators(alarmConfig.getId(), alarmConfigVo.getActuators());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteActuatorProperties(String actuatorId, AlarmActuatorType actuatorType) {
        switch (actuatorType) {
            case DEVICE_FUNCTION:
                invokeFunctionService.removeById(actuatorId);
                break;
            case NOTIFICATION:
                notificationService.removeById(actuatorId);
                break;
            case DATA_FORWARD:
                dataForwardService.removeById(actuatorId);
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + actuatorType);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delMain(String id) {
        triggerMapper.deleteByConfigId(id);
        alarmConfigMapper.deleteById(id);
        actuatorMapper.selectByConfigId(id).forEach(alarmConfigActuator -> {
            this.deleteActuatorProperties(alarmConfigActuator.getId(), alarmConfigActuator.getType());
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delBatchMain(Collection<? extends Serializable> idList) {
        for (Serializable id : idList) {
            triggerMapper.deleteByConfigId(id.toString());
            alarmConfigMapper.deleteById(id);
        }
    }

    @Override
    public AlarmConfigVo buildAlarmConfigVoById(String id) {
        final AlarmConfig alarmConfig = this.getById(id);
        log.info("alarmConfig >>> {}", alarmConfig);
        AlarmConfigVo alarmConfigVo = new AlarmConfigVo(alarmConfig);

        LambdaQueryWrapper<AlarmConfigTrigger> queryTriggerWrapper = new LambdaQueryWrapper<>();
        queryTriggerWrapper.eq(AlarmConfigTrigger::getAlarmConfigId, alarmConfig.getId());
        final List<AlarmConfigTriggerVo> triggerVos = triggerMapper.selectList(queryTriggerWrapper)
                .stream()
                .map(AlarmConfigTriggerVo::new)
                .collect(Collectors.toList());
        alarmConfigVo.setTriggers(triggerVos);

        LambdaQueryWrapper<AlarmConfigActuator> queryActuatorWrapper = new LambdaQueryWrapper<>();
        queryActuatorWrapper.eq(AlarmConfigActuator::getAlarmConfigId, alarmConfig.getId());
        final List<AlarmConfigActuatorVo> actuatorVos = actuatorMapper.selectList(queryActuatorWrapper)
                .stream()
                .map(AlarmConfigActuator::vo)
                .collect(Collectors.toList());
        actuatorVos.forEach(alarmConfigActuatorVo -> {
            log.info("alarmConfigActuatorVo >>> {}", alarmConfigActuatorVo);
            switch (alarmConfigActuatorVo.getActuatorType()) {
                case NOTIFICATION:
                    ActuatorNotification notification = notificationService.getById(alarmConfigActuatorVo.getId());
                    log.info("notification >>>> {}", notification);
                    if (notification != null) {
                        alarmConfigActuatorVo.setNotification(notification.vo());
                    }
                    break;
                case DEVICE_FUNCTION:
                    ActuatorInvokeFunction invokeFunction = invokeFunctionService.getById(alarmConfigActuatorVo.getId());
                    if (invokeFunction != null) {
                        alarmConfigActuatorVo.setInvokeFunction(invokeFunction.vo());
                    }
                    break;
                case DATA_FORWARD:
                    ActuatorDataForward dataForward = dataForwardService.getById(alarmConfigActuatorVo.getId());
                    if (dataForward != null) {
                        alarmConfigActuatorVo.setDataForward(dataForward.vo());
                    }
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + alarmConfigActuatorVo.getActuatorType());
            }
        });
        alarmConfigVo.setActuators(actuatorVos);

        log.info("alarmConfigVo >>>> {}", alarmConfigVo);
        return alarmConfigVo;
    }

    @Override
    public List<AlarmConfigTrigger> listDataReceiveFilter(String instanceId, String dataCode) {
        final DeviceData deviceData = dataService.getByCode(dataCode);
        return alarmConfigMapper.selectFilter(instanceId, deviceData.getId());
    }

    @Override
    public void invokeAlarmConfig(String instanceId, String reportDataId, Map<String, Object> dataMap) {
        queryByInstanceId(instanceId).forEach(alarmConfig -> {
            String ruleExpre = this.generateRuleExpre(alarmConfig.getId(), MessageEventType.DATA_RECEIVE, dataMap.keySet());
            this.markAlarmState(ruleExpre, dataMap);
            /* 调用规则表达式 */
            Boolean method = (Boolean) DyMethodUtil.invokeMethod(ruleExpre, dataMap);
            if (method) {
                if (alarmConfig.getIsSave()) {
                    /* 保存告警记录 */
                    dataMap.put("type", alarmConfig.getAlarmLevel().getValue());
                    dataMap.put("ruleExpre", ruleExpre);
                    alarmRecordService.save(alarmConfig, dataMap, reportDataId);
                }
                /* 满足调用规则表达式时，调用执行器（多个）*/
                this.invokeActuators(alarmConfig, dataMap);
            }
        });
    }

    @Override
    @Transactional
    public boolean deleteByInstanceId(String instanceId) {
        this.queryByInstanceId(instanceId).forEach(alarmConfig -> {
            // 1、删除告警执行器
            actuatorMapper.deleteByConfigId(alarmConfig.getId());
            // 2、删除告警触发器
            triggerMapper.deleteByConfigId(alarmConfig.getId());
            // 3、删除告警配置
            this.alarmConfigMapper.deleteById(alarmConfig.getId());
        });

        log.info("成功删除告警配置");
        return false;
    }


    @Override
    public List<AlarmConfig> queryByInstanceId(String instanceId) {
        return alarmConfigMapper.selectList(
                Wrappers.lambdaQuery(AlarmConfig.class)
                        .eq(AlarmConfig::getInstanceId, instanceId)
                        .eq(AlarmConfig::getState, StartStopState.STARTED)
        );
    }

    @Override
    public Boolean invokeTriggerRule(String alarmConfigId, Map<String, Object> dataMap) {
        /* 生成规则表达式 */
        String ruleExpre = generateRuleExpre(alarmConfigId, MessageEventType.DATA_RECEIVE, dataMap.keySet());
        markAlarmState(ruleExpre, dataMap);
        /* 调用规则表达式 */
        Object method = DyMethodUtil.invokeMethod(ruleExpre, dataMap);
//        dataMap.put("rule", ruleExpre);
        return (Boolean) method;
    }

    /**
     * 标注数据项为告警状态
     */
    private void markAlarmState(String ruleExpre, Map<String, Object> dataMap) {
        dataMap.keySet().stream()
                .filter(key -> !ruleExpre.contains(key))
                .peek(dataMap::remove);
    }

    @Override
    public String generateRuleExpre(String alarmConfigId, MessageEventType triggerSource, Set<String> dataCode) {
        return triggerMapper
                .selectList(
                        Wrappers.lambdaQuery(AlarmConfigTrigger.class)
                                .eq(AlarmConfigTrigger::getAlarmConfigId, alarmConfigId)
                                .eq(AlarmConfigTrigger::getSource, triggerSource))
                .stream()
                .map(AlarmConfigTriggerVo::new)
                .map(triggerVo -> triggerVo.getFilterExpre(dataCode))
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.joining("&&"));
    }

    @Override
    public void invokeActuators(AlarmConfig alarmConfig, Map<String, Object> dataMap) {
        // 将AlarmConfig信息转化成map格式，并放入dataMap
        BeanUtil.beanToMap(alarmConfig, dataMap, false, true);
        actuatorMapper
                .selectList(Wrappers.lambdaQuery(AlarmConfigActuator.class).eq(AlarmConfigActuator::getAlarmConfigId, alarmConfig.getId()))
                .forEach(actuator -> ActuatorContext.getInstance(actuator.getType()).invoke(actuator.getId(), dataMap));
    }
}
